------------Decimal Dungeon------------
A 4am crack                  2018-02-18
---------------------------------------

Name: Decimal Dungeon
Genre: educational
Year: 1985
Credits: David Howard Flatman, June
  Selena Stark
Publisher: Unicorn Software
Platform: Apple ][+ or later
Media: 5.25-inch disk
Sides: 1
OS: DOS 3.3
Previous cracks: none

                   ~

               Chapter 0
      In Which We Get Right To It


COPYA gives no errors, but the copy
reboots after loading DOS. I made a bit
copy onto a .nib disk image with EDD 4
and a CFFA 3000 card. That copy also
reboots.

In my experience, disks do not reboot
unless someone tells them to.

Inspecting track 0 with a sector editor
shows a normal DOS 3.3 bootloader, but
things are on the "wrong" sectors
because the physical-to-logical sector
mapping is non-standard.

On T00,S08 is the second-stage boot
code loaded at $B700, and we find this:

                 --v--

T00,S08 ($B700)
----------- DISASSEMBLY MODE ----------
002D:4A             LSR
002E:4A             LSR
002F:AA             TAX
0030:A9 00          LDA   #$00
0032:9D F8 04       STA   $04F8,X
0035:9D 78 04       STA   $0478,X
0038:20 00 BC       JSR   $BC00   <-- !
003B:A2 FF          LDX   #$FF
003D:9A             TXS
003E:8E EB B7       STX   $B7EB
0041:4C C8 BF       JMP   $BFC8
0044:20 89 FE       JSR   $FE89
0047:4C 84 9D       JMP   $9D84

                 --^--

Normally the JSR at $B738 calls $B793
to read DOS into memory.

$BC00 is stored on T00,S0D.

                 --v--

T00,S0D ($BC00)
----------- DISASSEMBLY MODE ----------
; copy most of $BB00 to lower memory
; (but don't overwrite the page 3
; vectors at $3D0+)
0000:A0 CF          LDY   #$CF
0002:B9 00 BB       LDA   $BB00,Y
0005:99 00 03       STA   $0300,Y
0008:88             DEY
0009:C0 FF          CPY   #$FF
000B:D0 F5          BNE   $0002
000D:EA             NOP
000E:EA             NOP
000F:EA             NOP

; exit via $B793 to load DOS as usual
0010:4C 93 B7       JMP   $B793

                 --^--

So we're stealthily putting some code
at $0300 but not calling it. Presumably
this is what gets called later.

$BB00 is stored on T00,S0C.

                 --v--

T00,S0C ($BB00, later $0300)
----------- DISASSEMBLY MODE ----------
; set up RWTS parameter table with
; track 7
0000:A9 07          LDA   #$07
0002:8D EC B7       STA   $B7EC

; command 0 (seek)
0005:A9 00          LDA   #$00
0007:8D F4 B7       STA   $B7F4
000A:8D F2 B7       STA   $B7F2

; call RWTS to seek to track 7
000D:20 E3 03       JSR   $03E3
0010:20 D9 03       JSR   $03D9

; turn on drive motor manually
0013:AE E9 B7       LDX   $B7E9
0016:BD 89 C0       LDA   $C089,X

0019:A9 00          LDA   #$00
001B:85 3C          STA   $3C
001D:A9 10          LDA   #$10
001F:85 3E          STA   $3E
0021:C6 3E          DEC   $3E
0023:D0 06          BNE   $002B
0025:A9 10          LDA   #$10
0027:85 3E          STA   $3E
0029:D0 7E          BNE   $00A9

; find next available address field
002B:20 44 B9       JSR   $B944
002E:B0 F1          BCS   $0021

; get sector number from address field
0030:A5 2D          LDA   $2D

; compare to the counter we initialized
; earlier (at $031B)
0032:C5 3C          CMP   $3C
0034:D0 EB          BNE   $0021

; find $EB nibble
0036:BD 8C C0       LDA   $C08C,X
0039:10 FB          BPL   $0036
003B:C9 EB          CMP   #$EB
003D:D0 6A          BNE   $00A9

; call an "RTS" to burn CPU cycles
; (the JSR burns 6 cycles, and the RTS
; burns another 6 cycles)
003F:20 C3 BC       JSR   $BCC3

; burn another 12 cycles
0042:20 C3 BC       JSR   $BCC3

; burn another 10 cycles (2 x 5)
0045:EA             NOP
0046:EA             NOP
0047:EA             NOP
0048:EA             NOP
0049:EA             NOP

; find next nibble
004A:BD 8C C0       LDA   $C08C,X
004D:10 FB          BPL   $004A

; must be $F2
004F:C9 F2          CMP   #$F2
0051:D0 56          BNE   $00A9

; increment sector counter
0053:E6 3C          INC   $3C

; do this for all 16 sectors on track 7
0055:A5 3C          LDA   $3C
0057:C9 10          CMP   #$10
0059:D0 D0          BNE   $002B

; now do it all over again for all
; tracks (down to 0)
005B:CE EC B7       DEC   $B7EC
005E:10 AD          BPL   $000D

                 --^--

So it's all about a timing bit. After
finding the $EB nibble (at $0336), we
intentionally burn 32 CPU cycles before
we even start looking for the $F2
nibble. Looking at the raw track in
Copy II Plus nibble editor, the $F2
nibble immediately follows the $EB. 32
CPU cycles is enough time that the $F2
ought to have come into and gone out of
the data latch. We ought to miss it.

Except...

If there is a hidden timing bit after
the $EB nibble, the data latch will
hold its value 4 cycles longer. By the
time we get around to checking for the
$F2 nibble, it will still be there --
just barely, but the timing works out.

Except on a copy which didn't preserve
the timing bit. Then we'll miss the $F2
nibble at $034F, try a few more times,
hit the same problem every time, and
eventually give up and reboot.

As far as I can tell, there is only 1
timing bit after the $EB nibble, so it
could be possible to make a protected
backup onto a physical floppy, given a
sufficiently advanced bit copy program
like EDD 4. But that doesn't get us any
closer to the goal of seamless digital
distribution. It is 2018, after all.
It's .dsk or bust.

                   ~

               Chapter 1
        But Wait, There's More


So we disable this copy protection
routine and move on with our lives,
right? Not so fast.

Continuing from $0360...

                 --v--

; set up an entirely new counter
0060:A0 40          LDY   #$40
0062:8C F2 B7       STY   $B7F2
0065:88             DEY
0066:D0 05          BNE   $006D
0068:CE F2 B7       DEC   $B7F2
006B:F0 49          BEQ   $00B6

; find a custom nibble sequence on the
; final track we checked for the timing
; bit (track 0 at this point)
; $CA $D2 $A9
006D:BD 8C C0       LDA   $C08C,X
0070:10 FB          BPL   $006D
0072:C9 CA          CMP   #$CA
0074:D0 EF          BNE   $0065
0076:EA             NOP
0077:BD 8C C0       LDA   $C08C,X
007A:10 FB          BPL   $0077
007C:C9 D2          CMP   #$D2
007E:D0 F2          BNE   $0072
0080:EA             NOP
0081:BD 8C C0       LDA   $C08C,X
0084:10 FB          BPL   $0081
0086:C9 A9          CMP   #$A9
0088:D0 E8          BNE   $0072

; read and decode a pair of 4-and-4
; encoded values
008A:A0 01          LDY   #$01
008C:BD 8C C0       LDA   $C08C,X
008F:10 FB          BPL   $008C
0091:2A             ROL
0092:85 2D          STA   $2D
0094:BD 8C C0       LDA   $C08C,X
0097:10 FB          BPL   $0094
0099:25 2D          AND   $2D

; store the values in zero page
009B:99 3C 00       STA   $003C,Y
009E:88             DEY
009F:10 EB          BPL   $008C

; turn off drive motor and return
00A1:BD 88 C0       LDA   $C088,X
00A4:A9 00          LDA   #$00
00A6:85 48          STA   $48
00A8:60             RTS

                 --^--

Turning again to the Copy II Plus
nibble editor, I can see this custom
prologue and encoded values:

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK: 00  START: 1800  LENGTH: 3DFF

3918: FC FF FF FF FF FF FF FF   VIEW
3920: FF FF FF FF FF FF FF FF
3928: FF FF FF FF FF FF FF FF
3930: FF FF FF FF FF FF FF FF
3938: FF FF FF FF FF FF FF FF
3940: CA D2 A9 AA AA AA AA AA
      ^^^^^^^^ ^^^^^^^^^^^
      prologue   encoded zeroes

3948: AA AA AA AA AA AA AA F7
3950: E7 F9 FE FF FE FF FF FF
3958: FF FF FF FF FF D5 AA 96

                 --^--

So this protection check does have side
effects after all. It sets zero page
$3C and $3D to 0. ($AA $AA is the
4-and-4 encoded value 0.)

Thus:

T00,S0C,$00: -> A9 00 85 3C 85 3D 60

which looks like

0000:A9 00          LDA   #$00
0002:85 3C          STA   $3C
0004:85 3D          STA   $3D
0006:60             RTS

and unconditionally sets the two zero
page locations to 0 and exits without
checking timing bits or other stuff.

]PR#6
...works, and it is glorious...

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 1698
------------------EOF------------------
